package main

import (
	// 标准库
	"context"
	"flag"
	"fmt"
	eclient "go.etcd.io/etcd/client/v3"
	eresolver "go.etcd.io/etcd/client/v3/naming/resolver"
	"google.golang.org/grpc/balancer/roundrobin"
	"log"
	"time"

	// grpc 桩文件
	//"github.com/grpc_demo/proto"

	proto "5_etcd_exp/helloworld"
	//"./helloworld"

	// grpc
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

const (
	MyService   = "xiaoxu/demo"
	defaultName = "world"
)

var (
	addr = flag.String("addr", "localhost:8080", "the address to connect to")
	name = flag.String("name", defaultName, "name to client ")
)

// 1. 启动入口
func main() {
	// 1. 创建 etcd 客户端
	etcdClient, err := eclient.NewFromURL("my_etcd_url")
	if err != nil {
		log.Fatalf("could not create etcd client %v", err)
	}

	// 2. 创建 etcd 实现的 grpc 服务注册发现模块 resolver
	etcdResolverBuilder, err := eresolver.NewBuilder(etcdClient)
	if err != nil {
		log.Fatalf("could not create etcd resolver %v", err)
	}
	//
	//// 拼接服务名称，需要固定义 etcd:/// 作为前缀
	etcdTarget := fmt.Sprintf("etcd:///%s", MyService)

	// 创建 grpc 连接代理
	//conn, err := grpc.Dial(
	conn, err := grpc.NewClient(
		// 服务名称
		etcdTarget,
		// 注入 etcd resolver
		grpc.WithResolvers(etcdResolverBuilder),
		// 声明使用的负载均衡策略为 roundrobin
		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, roundrobin.Name)),
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		log.Fatalf("could not create grpc connection %v", err)
	}
	//defer conn.Close()

	// 创建 grpc 客户端
	client := proto.NewHelloServiceClient(conn)

	//conn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	//client := proto.NewHelloServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*100)
	defer cancel()
	r, err := client.SayHello(ctx, &proto.HelloReq{Name: "xiaolin"})
	if err != nil {
		log.Fatalf("could not client to server sayHello: %v", err)
	}
	log.Printf("Client: %s", r.GetMessage())
	//for {
	//	// 发起 grpc 请求
	//	//resp, _ := client.SayHello(context.Background(), &proto.HelloReq{
	//	//	Name: "xiaoxuxiansheng",
	//	//})
	//	resp, _ := client.SayHello(ctx, &proto.HelloReq{
	//		Name: "xiaoxuxiansheng",
	//	})
	//	fmt.Printf("resp: %+v", resp)
	//	// 每隔 1s 发起一轮请求
	//	<-time.After(time.Second * 10)
	//	//}
	//}

	// 2.注入 etcd resolver
	// 在 grpc 客户端启动时，首先会获取到 etcd 中提供的 grpc 服务发现构造器 resolverBuilder，然后在调用 grpc.Dial 方法创建连接代理 ClientConn 时，将其注入其中.

	// 3. 启动 grpc balancerWrapper
	// 在 grpc-go 客户端启动时，会调用方法链 DialContext -> newCCBalancerWrapper -> go ccBalancerWrapper.watcher，启动一个 balancerWrapper 的守护协程，持续监听 ClientConn 更新、balancer 更新等事件并进行处理.

	// 4. 获取etcd resolver builder
	// 在 grpc-go 客户端启动时，还有一条方法链路是 DialContext -> ClientConn.parseTargetAndFindResolver -> ClientConn.getResolver，通过 target 的 schema（etcd），读取此前通过 option 注入的 etcd resolverBuilder.

	// 5. 创建并启动etcd resolver
	// 在取得 etcd resolver builder 后，会在 newCCResolverWrapper 方法中，执行 builder.Build 方法进行 etcd resolver 的创建.

	// 6. 接收endpoint更新事件

	// 7. 启用roundrobin balancer
	// 经由 ClientConn.maybeApplyDefaultServiceConfig -> ClientConn.applyServiceConfigAndBalancer -> balancerWrapper.switchTo 的方法链路，会通过 grpc 客户端启动时注入的 defaultServiceConfig 中获取本次使用的负载均衡策略名 "round_robin"，接下来会调用 ccBalancerWrapper.switchTo 方法，将当前使用的负载均衡器切换成 round-robin 类型.

	// 8. 更新endpoint

	// 9. grpc客户端请求

	// 10. roundrobin负载均衡

	// 总结： grpc 客户端启动过程中会依次构造 balancer 和 resolver，并开启守护协程对状态变更事件进行监听和处理构造 etcd resolver 并开启守护协程 watcher 进行服务端 endpoints 更新事件的监听负载均衡器 balancer 的守护协程会接收到来自 resolver 传递的 endpoints 列表，对本地缓存的数据进行更新grpc 客户端发起请求时，会通过 round-robin 算法，对负载均衡器中缓存的 endpoints 轮询使用
}
